using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FoundationLib.AddIns.Constraints
{
	/// <summary>
	/// Represents the root of the EA add-in
	/// </summary>
	public class Root
	{
		/// <summary>
		/// Creates a new instance
		/// </summary>
		public Root()
		{
		}

		/// <summary>
		/// Called by Enterprise Architect upon connect
		/// </summary>
		/// <param name="repository">The repository</param>
		/// <returns>An empty string</returns>
		public string EA_Connect(EA.Repository repository)
		{
			return String.Empty;
		}

		/// <summary>
		/// Called by Enterprise Architect upon disconnect
		/// </summary>
		public void EA_Disconnect()
		{
			GC.Collect();
			GC.WaitForPendingFinalizers();
		}

		private T ExtractArgument<T>(object args, int index)
		{
			object[] argsArray = args as object[];
			if (argsArray == null)
				return default(T);
			if (index >= argsArray.Length)
				return default(T);
			return (T)argsArray[index];
		}

		private void EnsureValidArguments(object args, int expectedArgsLength)
		{
			if (args == null)
				throw new ArgumentNullException("args");
			object[] argsArray = args as object[];
			if (argsArray == null)
				throw new ArgumentException("Invalid number of arguments passed.", "args");
			if (argsArray.Length < expectedArgsLength)
				throw new ArgumentException("Invalid number of arguments passed.", "args");
		}

		/// <summary>
		/// Processes all invariants defined on class elements
		/// </summary>
		/// <param name="repository">The EA repository</param>
		/// <param name="args">Arguments passed to the add-in</param>
		public object ProcessInvariant(EA.Repository repository, object args)
		{
			this.EnsureValidArguments(args, 2);

			string indent = this.ExtractArgument<string>(args, 0);
			string classGuid = this.ExtractArgument<string>(args, 1);
			string resourcePrefix = this.ExtractArgument<string>(args, 2);
			if (!String.IsNullOrWhiteSpace(resourcePrefix))
				Context.Current.ResourcePrefix = resourcePrefix;

			OclTransformer transformer = OclTransformerFactory.Create(repository.GetElementByGuid(classGuid));
			return String.Join(Environment.NewLine, transformer.Transform().Select(x => String.Concat(indent, x)));
		}

		private object ProcessConditions(EA.Repository repository, object args, MethodConstraintType type)
		{
			this.EnsureValidArguments(args, 3);

			string indent = this.ExtractArgument<string>(args, 0);
			string classGuid = this.ExtractArgument<string>(args, 1);
			string operationGuid = this.ExtractArgument<string>(args, 2);
			string resourcePrefix = this.ExtractArgument<string>(args, 3);
			if (!String.IsNullOrWhiteSpace(resourcePrefix))
				Context.Current.ResourcePrefix = resourcePrefix;

			OclTransformer transformer = OclTransformerFactory.Create(repository.GetMethodByGuid(operationGuid), type);
			return String.Join(Environment.NewLine, transformer.Transform().Select(x => String.Concat(indent, x)));
		}

		/// <summary>
		/// Processes all pre-conditions defined on a method
		/// </summary>
		/// <param name="repository">The EA repository</param>
		/// <param name="args">Arguments passed to the add-in</param>
		public object ProcessPreConditions(EA.Repository repository, object args)
		{
			return this.ProcessConditions(repository, args, MethodConstraintType.PreCondition);
		}

		/// <summary>
		/// Processes all post-conditions defined on a method
		/// </summary>
		/// <param name="repository">The EA repository</param>
		/// <param name="args">Arguments passed to the add-in</param>
		public object ProcessPostConditions(EA.Repository repository, object args)
		{
			return this.ProcessConditions(repository, args, MethodConstraintType.PostCondition);
		}
	}
}
